package cn.xueden.common.redis.utils;

import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.commands.JedisCommands;
import redis.clients.jedis.params.SetParams;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.UUID;

@Slf4j
@Component
public class RedisDistributedLock {

    public static final String UNLOCK_LUA;
    private final Logger logger = LoggerFactory.getLogger(RedisDistributedLock.class);
    private RedisTemplate<Object, Object> redisTemplate;
    private ThreadLocal<String> lockFlag = new ThreadLocal();


    static {
        StringBuilder sb = new StringBuilder();
        sb.append("if redis.call(\"get\",KEYS[1]) == ARGV[1] ");
        sb.append("then ");
        sb.append("    return redis.call(\"del\",KEYS[1]) ");
        sb.append("else ");
        sb.append("    return 0 ");
        sb.append("end ");
        UNLOCK_LUA = sb.toString();
    }


    public RedisDistributedLock(RedisTemplate<Object, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    public boolean lock(String key, long expire, int retry, long sleepMillis) {
        boolean result;
        for(result = this.setRedis(key, expire); !result && retry-- > 0; result = this.setRedis(key, expire)) {
            try {
                Thread.sleep(sleepMillis);
            } catch (InterruptedException var9) {
                return false;
            }
        }

        return result;
    }

    public boolean releaseLock(String key) {
        try {
            final List<String> keys = new ArrayList();
            keys.add(key);
            final List<String> args = new ArrayList();
            args.add(this.lockFlag.get());
            Long result = (Long)this.redisTemplate.execute(new RedisCallback<Long>() {
                public Long doInRedis(RedisConnection connection) throws DataAccessException {
                    Object nativeConnection = connection.getNativeConnection();
                    if (nativeConnection instanceof JedisCluster) {
                        return (Long)((JedisCluster)nativeConnection).eval(RedisDistributedLock.UNLOCK_LUA, keys, args);
                    } else {
                        return nativeConnection instanceof Jedis ? (Long)((Jedis)nativeConnection).eval(RedisDistributedLock.UNLOCK_LUA, keys, args) : 0L;
                    }
                }
            });
            return result != null && result > 0L;
        } catch (Exception var5) {
            this.logger.info(key + "底层解锁发生异常{}", var5);
            return false;
        }
    }

    private boolean setRedis(final String key, final long expire) {
        try {
            String result = (String)this.redisTemplate.execute(new RedisCallback<String>() {
                public String doInRedis(RedisConnection connection) throws DataAccessException {
                    JedisCommands commands = (JedisCommands)connection.getNativeConnection();
                    String uuid = UUID.randomUUID().toString();
                    RedisDistributedLock.this.lockFlag.set(uuid);
                    SetParams setParams = SetParams.setParams().nx().px(expire);
                    return commands.set(key, uuid, setParams);
                }
            });
            return Objects.nonNull(result) && "OK".equalsIgnoreCase(result);
        } catch (Exception var5) {
            this.logger.info(key + "底层设置到redis发生异常{}", var5);
            return false;
        }
    }


}
